package com.songmao.pricecloudapitest.requestcore;

import com.songmao.pricecloudapitest.config.PriceCloudApiConfig;
import com.songmao.pricecloudapitest.utils.LoggerCore;
import com.songmao.pricecloudapitest.utils.ParsingJson;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.*;

/**
 * @author Remy
 * @description : 用于Http核心请求类
 * @date 2020-04-03 12:28
 */
@Component
public class HttpReuqestCore {
    private static final int REDIRECTCODE = 302;


    LoggerCore loggerCore = new LoggerCore(this.getClass());

    private PriceCloudApiConfig apiTimeOutConfig;

    @Autowired
    public void setApiTimeOutConfig(PriceCloudApiConfig apiTimeOutConfig) {
        this.apiTimeOutConfig = apiTimeOutConfig;
    }

    private CloseableHttpClient httpClient;

    @PostConstruct
    private void afterConstructor() {
        //判断apiTimeOutConfig是否为空，如果为空则不作任意处理;直接实例化一个默认HttpClient
        Optional<PriceCloudApiConfig> optional = Optional.ofNullable(apiTimeOutConfig);
        if (optional.isPresent()) {
            int connectTimeout = Integer.parseInt(apiTimeOutConfig.getConnectTimeout());
            int socketTimeout = Integer.parseInt(apiTimeOutConfig.getSocketTimeout());
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(connectTimeout).setConnectTimeout(socketTimeout).
                    build();
            httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
        } else {
            httpClient = HttpClients.createDefault();
        }

    }

    private static HttpClientContext createClinetContext() {
        return HttpClientContext.create();
    }

    public ResponePoJo get(String url, String... headers) {
        try {
            loggerCore.info("request start");
            HttpGet get = (HttpGet) createHttpRequestType(RequestTypeEnum.GET, url);
            return executeRequest(get, headers);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo get(String url, Map<String, String> heardsMap) {
        try {
            HttpGet get = (HttpGet) createHttpRequestType(RequestTypeEnum.GET, url);
            return executeRequest(get, heardsMap);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }

    }

    public ResponePoJo get(String url, HeaderImpl header) {
        try {
            HttpGet get = (HttpGet) createHttpRequestType(RequestTypeEnum.GET, url);
            return this.executeRequest(get, header);

        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo get(String url, HeaderImpl[] headers) {
        try {
            HttpGet get = (HttpGet) createHttpRequestType(RequestTypeEnum.GET, url);
            //获取响应状态
            return this.executeRequest(get, headers);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo post(String url, Map<String, String> params, String... heards) {
        try {
            HttpPost post = (HttpPost) createHttpRequestType(RequestTypeEnum.POST, url);
            //设置请求参数
            List<NameValuePair> ps = new ArrayList<>();
            for (String key : params.keySet()) {
                ps.add(new BasicNameValuePair(key, params.get(key)));
            }
            try {
                post.setEntity(new UrlEncodedFormEntity(ps));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return ResponePoJo.createErrorResponePoJo();
            }

            ResponePoJo pojo = executeRequest(post, heards);
            if (pojo.getStatus() == REDIRECTCODE) {
                HttpGet get = (HttpGet) createHttpRequestType(RequestTypeEnum.GET, pojo.getResponseMap().get("Location"));
                return executeRequest(get, null);
            }
            return pojo;
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }

    }

    public ResponePoJo post(String url, Map<String, String> params, Map<String, String> headersMap) {
        try {
            HttpPost post = (HttpPost) createHttpRequestType(RequestTypeEnum.POST, url);
            //设置请求参数
            List<NameValuePair> ps = new ArrayList<>();
            for (String key : params.keySet()) {
                ps.add(new BasicNameValuePair(key, params.get(key)));
            }
            try {
                post.setEntity(new UrlEncodedFormEntity(ps));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return ResponePoJo.createErrorResponePoJo();
            }

            ResponePoJo pojo = executeRequest(post, headersMap);
            if (pojo.getStatus() == REDIRECTCODE) {
                HttpGet get = (HttpGet) createHttpRequestType(RequestTypeEnum.GET, pojo.getResponseMap().get("Location"));
                return executeRequest(get, null);
            }
            return pojo;
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
        //获取响应内容;

    }

    public ResponePoJo post(String url, String json, Map<String, String> headersMap) {
        try {
            HttpPost post = (HttpPost) createHttpRequestType(RequestTypeEnum.POST, url);
            headersMap.put("Content-Type", "application/json");
            loggerCore.info("json:{}", json);
            post.setEntity(new StringEntity(json));
            return executeRequest(post, headersMap);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }

    }

    public ResponePoJo post(String url, String json) {
        try {
            HttpPost post = (HttpPost) createHttpRequestType(RequestTypeEnum.POST, url);
            Map<String, String> headersMap = new HashMap<>(1);
            headersMap.put("Content-Type", "application/json");
            post.setEntity(new StringEntity(json));
            return executeRequest(post, headersMap);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }

    }

    public ResponePoJo put(String url, String json, Map<String, String> heardsMap) {
        try {
            HttpPut put = (HttpPut) createHttpRequestType(RequestTypeEnum.PUT, url);
            heardsMap.put("Content-Type", "application/json");
            loggerCore.info("json:{}", json);
            put.setEntity(new StringEntity(json));
            return executeRequest(put, heardsMap);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo put(String url, Header header) {
        try {
            HttpPut put = (HttpPut) createHttpRequestType(RequestTypeEnum.PUT, url);
            return executeRequest(put, header);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo put(String url, Header[] headers) {
        try {
            HttpPut put = (HttpPut) createHttpRequestType(RequestTypeEnum.PUT, url);
            return executeRequest(put, headers);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo delete(String url, String... headers) {
        try {
            HttpDelete delete = (HttpDelete) createHttpRequestType(RequestTypeEnum.DELETE, url);
            return executeRequest(delete, headers);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo delete(String url, Map<String, String> heardsMap) {
        try {
            HttpDelete delete = (HttpDelete) createHttpRequestType(RequestTypeEnum.DELETE, url);
            return executeRequest(delete, heardsMap);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo delete(String url, Header header) {
        try {
            HttpDelete delete = (HttpDelete) createHttpRequestType(RequestTypeEnum.DELETE, url);
            return executeRequest(delete, header);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    public ResponePoJo delete(String url, Header[] headers) {
        try {
            HttpDelete delete = (HttpDelete) createHttpRequestType(RequestTypeEnum.DELETE, url);
            return executeRequest(delete, headers);
        } catch (Exception e) {
            return ResponePoJo.createErrorResponePoJo();
        }
    }

    /**
     * 创建httpRequest对象
     *
     * @param:url
     * @return:requestType
     * @param:requestTypeEnum
     */
    private Object createHttpRequestType(RequestTypeEnum requestTypeEnum, String url) {
        HttpGet get;
        HttpPost post;
        HttpPut put;
        HttpDelete delete;

        switch (requestTypeEnum) {
            case GET:
                loggerCore.info("requestTypeEnum == get;requestTypeEnum:{}", requestTypeEnum);
                loggerCore.info("url:{}", url);
                get = new HttpGet(url);
                loggerCore.info("return get");
                return get;
            case POST:
                loggerCore.info("requestTypeEnum == post;requestTypeEnum:{}", requestTypeEnum);
                loggerCore.info("url:{}", url);
                post = new HttpPost(url);
                loggerCore.info("return post");
                return post;
            case PUT:
                loggerCore.info("requestTypeEnum == put;requestTypeEnum:{}", requestTypeEnum);
                loggerCore.info("url:{}", url);
                put = new HttpPut(url);
                loggerCore.info("return put");
                return put;
            case DELETE:
                loggerCore.info("requestTypeEnum == delete;requestTypeEnum:{}", requestTypeEnum);
                loggerCore.info("url:{}", url);
                delete = new HttpDelete(url);
                loggerCore.info("return delete");
                return delete;
            default:
                loggerCore.error("requestTypeEnum!=get、post、put、delete", requestTypeEnum);
                throw new RuntimeException("error requestTypeEnum");
        }
    }


    /**
     * 执行并且封装respone
     *
     * @return:ResponePoJo
     * @param:httpRequestType
     * @param:httpRequestHeards
     */
    private ResponePoJo executeRequest(Object httpRequestType, Object httpRequestHeards) {
        loggerCore.info("create HttpClientContext");
        HttpClientContext context = createClinetContext();

        loggerCore.info("if requestType:{}", httpRequestType);
        if (httpRequestType == null) {
            loggerCore.error("request is null;httpRequestType:{}", null);
            throw new RuntimeException("The request type is not get, post, put, delete; please check the request type");
        }

        class ProcessRequestHeards {
            //判断如何处理请求头
            private Header[] setHeards(Object httpRequestHeader) {
                //判断obj传进来的参数不为空
                if (httpRequestHeader != null) {
                    //判断是否为数组
                    loggerCore.info("if httpRequestHeader instanceof String[]; httpRequestHeader:{}", httpRequestHeader);
                    if (httpRequestHeader instanceof String[]) {

                        class StringConvertHeader {

                            private Header[] stringConvertHeader(String[] headers) {
                                loggerCore.info("if StringHeader[].length<=0; StringHeader:{}", headers);
                                if (headers.length == 0) {
                                    loggerCore.info("if String[].length<=0; return null");
                                    return null;
                                }

                                loggerCore.info("if StringHeader[].length==1; StringHeader:{}", headers);
                                //证明用户传的参数是["Content-Type:xxxxxx,Connection:Keep-Alive"]
                                String twoDelimiter = ":";
                                String oneDelimiter = ",";
                                Header[] h;
                                if (headers.length == 1) {
                                    loggerCore.info("StringHeader[].length==1;take out StringHeader:{}", headers);

                                    //按照逗号进行切割Content-Type:xxxxxx;Connection:Keep-Alive
                                    loggerCore.info("split:{}", oneDelimiter);

                                    String[] keyAndValue = headers[0].split(oneDelimiter);
                                    loggerCore.info("splitOver:{}", keyAndValue);

                                    //创建数组;长度为切割后的长度;
                                    loggerCore.info("create HeaderImpl;HeaderImpllength==keyAndValue.size:{}", keyAndValue.length);
                                    h = new HeaderImpl[keyAndValue.length];


                                    for (int i = 0; i < keyAndValue.length; i++) {
                                        String k = keyAndValue[i];
                                        String[] value = k.split(twoDelimiter);
                                        HeaderImpl header = new HeaderImpl();
                                        header.setNameAndValue(value[0], value[1]);
                                        h[i] = header;
                                    }

                                    return h;
                                }

                                //证明用户传的参数是["Content-Type:xxxxxx","Connection:Keep-Alive"]
                                h = new HeaderImpl[headers.length];
                                for (int i = 0; i < headers.length; i++) {
                                    String temp = headers[i];
                                    String[] s = temp.split(twoDelimiter);
                                    HeaderImpl header = new HeaderImpl();
                                    header.setNameAndValue(s[0], s[1]);
                                    h[i] = header;
                                }
                                return h;

                            }
                        }

                        String[] stringHeaders = (String[]) httpRequestHeader;


                        return new StringConvertHeader().stringConvertHeader(stringHeaders);

                    }

                    loggerCore.info("if httpRequestHeader instanceof Map; httpRequestHeader:{}", httpRequestHeader);
                    if (httpRequestHeader instanceof Map) {
                        Map<String, String> headerMap = (Map<String, String>) httpRequestHeader;
                        class MapConvertHeader {
                            private Header[] mapConvertHeader(Map<String, String> headerMap) {
                                if (headerMap != null && headerMap.size() > 0) {
                                    Header[] h = new HeaderImpl[headerMap.size()];
                                    int i = 0;
                                    for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                                        HeaderImpl header = new HeaderImpl();
                                        header.setNameAndValue(entry.getKey(), entry.getValue());
                                        h[i] = header;
                                        i++;
                                    }
                                    return h;
                                }
                                return null;
                            }
                        }
                        return new MapConvertHeader().mapConvertHeader(headerMap);
                    }

                    loggerCore.info("if httpRequestHeader instanceof Header; httpRequestHeader:{}", httpRequestHeader);
                    if (httpRequestHeader instanceof Header) {
                        HeaderImpl header = (HeaderImpl) httpRequestHeader;
                        if (!"".equals(header.getName()) || !"".equals(header.getValue())) {
                            return new HeaderImpl[]{header};
                        }
                        return null;
                    }

                    loggerCore.info("if httpRequestHeader instanceof Header[]; httpRequestHeader:{}", httpRequestHeader);
                    if (httpRequestHeader instanceof Header[]) {
                        HeaderImpl[] header = (HeaderImpl[]) httpRequestHeader;
                        if (header.length > 1) {
                            return header;
                        }
                        return header;
                    }
                }

                loggerCore.info("httpRequestHeader is null; httpRequestHeader:{}", httpRequestHeader);

                return null;
            }
        }
        ProcessRequestHeards processRequestHeards = new ProcessRequestHeards();
        Header[] headers = processRequestHeards.setHeards(httpRequestHeards);
        loggerCore.info("headers:{}", headers);

        if (httpRequestType instanceof HttpGet) {

            HttpGet get = (HttpGet) httpRequestType;
            if (headers != null && headers.length > 0) {
                loggerCore.info("setHeaders:{}", headers);
                get.setHeaders(headers);
            }

            try {
                loggerCore.info("execute request");
                httpClient.execute(get, context);
                return this.setResponseBodyAndResponseHeard(context);
            } catch (IOException e) {
                loggerCore.error("error：{}", e);
                e.printStackTrace();
            } finally {
                get.releaseConnection();
            }
        }
        if (httpRequestType instanceof HttpPost) {
            HttpPost post = (HttpPost) httpRequestType;
            try {
                if (headers != null && headers.length > 0) {
                    loggerCore.info("setHeaders:{}", headers);
                    post.setHeaders(headers);
                }
                loggerCore.info("execute request");
                httpClient.execute(post, context);
                return this.setResponseBodyAndResponseHeard(context);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                post.releaseConnection();
            }
        }
        if (httpRequestType instanceof HttpPut) {
            HttpPut put = (HttpPut) httpRequestType;
            try {
                if (headers != null && headers.length > 0) {
                    loggerCore.info("setHeaders:{}", headers);
                    put.setHeaders(headers);
                }
                loggerCore.info("execute request");
                httpClient.execute(put, context);
                return this.setResponseBodyAndResponseHeard(context);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                put.releaseConnection();
            }
        }
        if (httpRequestType instanceof HttpDelete) {
            HttpDelete delete = (HttpDelete) httpRequestType;
            try {
                if (headers != null && headers.length > 0) {
                    loggerCore.info("setHeaders:{}", headers);
                    delete.setHeaders(headers);
                }
                loggerCore.info("execute request");
                httpClient.execute(delete, context);
                return this.setResponseBodyAndResponseHeard(context);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                delete.releaseConnection();
            }
        }
        throw new RuntimeException("error executeRequest");
    }


    /**
     * @return ResponePoJo
     * @description: 传入json以及respone对象； 会自动设置pojo对象；
     */
    private ResponePoJo setResponseBodyAndResponseHeard(HttpClientContext context) {
        Object data = null;
        if (context != null) {
            class ProcessAllMessage {

                private HttpClientContext context;

                public ProcessAllMessage(HttpClientContext context) {
                    this.context = context;
                }

                public Map<String, String> getCookie() {
                    Map<String, String> cookieMap = null;
                    List<Cookie> cookieList = context.getCookieStore().getCookies();
                    if (cookieList != null && cookieList.size() > 0) {
                        cookieMap = new HashMap<>(cookieList.size());
                        for (Cookie cookie : cookieList) {
                            cookieMap.put(cookie.getName(), cookie.getValue());
                        }
                    }
                    return cookieMap;
                }

                //此方法主要用login的核心核心参数,302重定向
                private Map<String, String> splitUrlParameter() {
                    //拿到请求过程中全部重定向地址
                    List<URI> redirectLocations = context.getRedirectLocations();
                    if (redirectLocations != null && redirectLocations.size() > 0) {
                        Map<String, String> parameterMap = new HashMap<>(redirectLocations.size());
                        String url = redirectLocations.get(redirectLocations.size() - 1).toASCIIString();
                        parameterMap.put("Location", url);
                        String uri = redirectLocations.get(redirectLocations.size() - 1).getQuery();
                        //分割URL地址字符串的分割符号 name=zhangsan&age=14&
                        String splitSymbol = "&";
                        //分割具体参数的key和value的分割符号; to_appid=appf820087ade4b
                        String splitKeyAndValueSymbol = "=";
                        if (uri != null) {
                            String[] parameter = uri.split(splitSymbol);
                            for (String parameterKeyAndValue : parameter) {
                                if (parameterKeyAndValue.contains("appid")) {
                                    String[] appid = parameterKeyAndValue.split(splitKeyAndValueSymbol);
                                    parameterMap.put(appid[0] = appid[0] = "to_appid", appid[1]);
                                }
                                if (parameterKeyAndValue.contains("nonce")) {
                                    String[] nonceStr = parameterKeyAndValue.split(splitKeyAndValueSymbol);
                                    parameterMap.put(nonceStr[0] = "nonce_str", nonceStr[1]);
                                }
                                if (parameterKeyAndValue.contains("sign")) {
                                    String[] sign = parameterKeyAndValue.split(splitKeyAndValueSymbol);
                                    parameterMap.put(sign[0] = "sign", sign[1]);
                                }
                            }
                            parameterMap.put("Location", url);
                            return parameterMap;
                        }
                        return parameterMap;
                    }
                    return null;
                }

                private Map<String, String> splitResponseHeaders() {
                    Header[] responseHeaders = context.getResponse().getAllHeaders();
                    Map<String, String> responseHeadersMap;
                    if (responseHeaders != null && responseHeaders.length > 0) {
                        responseHeadersMap = new HashMap<>(responseHeaders.length);
                        for (Header header : responseHeaders) {
                            responseHeadersMap.put(header.getName(), header.getValue());
                        }
                        return responseHeadersMap;
                    }
                    throw new RuntimeException("headers is null" + Arrays.toString(responseHeaders));
                }
            }
            ProcessAllMessage processAllMessage = new ProcessAllMessage(context);
            try {
                //拿到cookie信息
                Map<String, String> cookieMap = processAllMessage.getCookie();
                loggerCore.info("getCookie; cookie:{}", cookieMap);

                //拿到请求过程中全部重定向地址
                Map<String, String> urlParameterMap = processAllMessage.splitUrlParameter();
                loggerCore.info("geturlParameterMap; urlParameterMap:{}", urlParameterMap);


                //拿到json;
                String json = EntityUtils.toString(context.getResponse().getEntity());
                loggerCore.info("getjson; json:{}", json);

                //获取respone
                HttpResponse response = context.getResponse();
                //获取响应码
                int status = response.getStatusLine().getStatusCode();
                loggerCore.info("getStatus; Status:{}", status);

                //判断是否是合法json
                String code = "0";
                if (ParsingJson.judgmentJson(json)) {
                    //如果是合法data,则取出json格式内的全部data
                    if (ParsingJson.getPathCode(json, apiTimeOutConfig.getDataPath()) != null) {
                        data = ParsingJson.getPathCode(json, apiTimeOutConfig.getDataPath());
                    }

                    if (ParsingJson.getPathCode(json, apiTimeOutConfig.getCodePath()) != null) {
                        code = Objects.requireNonNull(ParsingJson.getPathCode(json, apiTimeOutConfig.getCodePath())).toString();
                        loggerCore.info("getCode; getCode:{}", code);
                    } else {
                        code = "100";
                    }
                }

                //处理响应头
                Map<String, String> responseHeadersMap = processAllMessage.splitResponseHeaders();
                loggerCore.info("getresponseHeadersMap; responseHeadersMap:{}", responseHeadersMap);

                //将respone的全部和URL请求参数全部放入一个字典内;
                Map<String, String> newMap = new HashMap<>();
                if (urlParameterMap != null) {
                    newMap.putAll(urlParameterMap);
                }
                if (responseHeadersMap.size() > 0) {
                    newMap.putAll(responseHeadersMap);
                }

                loggerCore.info("return ResponePoJo; ResponePoJo:{}", ResponePoJo.createResponePoJo(code, data, status, newMap, cookieMap));
                return ResponePoJo.createResponePoJo(code, data, status, newMap, cookieMap);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new ResponePoJo();
    }


}



